﻿using System;
using System.Web;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.Navigation;
using System.Web.UI;

namespace SharePointHelper.DLL.SSOM
{
	public class Navigation
	{
		/// <summary>
		/// Get SharePoint dataSource for sitemap control
		/// If called from webPart, don't forget to add this to the control tree!
		/// </summary>
		/// <param name="siteMapProviderName">The SharePoint name of the sitemap provider (You can find in web.config)</param>
		/// <param name="datasourceId">Specify this is you intend to use javascript client object model to reference, otherwise leave blank</param>
		/// <returns></returns>
		public static PortalSiteMapDataSource GetSiteMapDataSource(string siteMapProviderName, string datasourceId)
		{
			PortalSiteMapDataSource siteMapDataSource = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(datasourceId)) { datasourceId = Guid.NewGuid().ToString(); }
				if (string.IsNullOrEmpty(siteMapProviderName))
				{ siteMapProviderName = SiteMapProviderName.GlobalNavSiteMapProvider.ToString(); }

				// get provider
				siteMapDataSource = new PortalSiteMapDataSource();
				siteMapDataSource.SiteMapProvider = siteMapProviderName;
				siteMapDataSource.ID = datasourceId;
			}
			catch (Exception ex)
			{
				Log.WriteError("htfjjgyhdyrsdxfzdf", ex);
				siteMapDataSource = null;
			}
			return siteMapDataSource;
		}
		/// <summary>
		/// Get a sitemap datasource from SharePoint
		/// </summary>
		/// <param name="siteMapProviderName"></param>
		/// <returns></returns>
		public static PortalSiteMapDataSource GetSiteMapDataSource(SiteMapProviderName siteMapProviderName)
		{
			// call original
			return GetSiteMapDataSource(siteMapProviderName.ToString(), null);
		}

		/// <summary>
		/// Common SiteMap Provider names for SharePoint Publishing
		/// </summary>
		public enum SiteMapProviderName
		{
			GlobalNavSiteMapProvider,
			CombinedNavSiteMapProvider,
			CurrentNavSiteMapProvider,
			CurrentNavSiteMapProviderNoEncode,
		}
		

		/// <summary>
		/// Create a tree view, using site map datasource id passed to bind data
		/// IMPORTANT: You will have to manually create and add the tvblank.png blank image to your /_layouts/images folder
		/// </summary>
		/// <param name="treeViewId">unique id assigend to this tree view. If null, will assign a GUID</param>
		/// <param name="dataSourceId">id for a datasource that has already beend defined and added to control tree. Leave null if using SharePoint sitemap portal provider</param>
		/// <param name="cssClass">class applied to the treeview. Microsoft default is ms-navitem</param>
		/// <param name="expandLevelsBelowRootNode">number of levels to expand below root node. 0 = none, 1 = 2 levels, the root level and 1 level below it.</param>
		/// <returns></returns>
		public static TreeView CreateTreeView(string dataSourceId, int expandLevelsBelowRootNode,
			bool isPopulateNodesOnDemand, bool isShowLines, string cssClass, string[] cssLevelStyles)
		{
			TreeView treeView = null;
			try
			{
				// init
				treeView = new TreeView();
				if (!string.IsNullOrEmpty(dataSourceId)) { treeView.DataSourceID = dataSourceId; }
				treeView.ExpandDepth = expandLevelsBelowRootNode;
				treeView.ShowLines = isShowLines;
				if (!isShowLines)
				{
					treeView.NoExpandImageUrl = "/_layouts/images/tvblank.png";
					treeView.CollapseImageUrl = "/_layouts/images/tvopen.png";
					treeView.ExpandImageUrl = "/_layouts/images/tvclosed.png";
				}
				treeView.SkipLinkText = "";
				treeView.PopulateNodesFromClient = isPopulateNodesOnDemand;
				treeView.PopulateNodesFromClient = isPopulateNodesOnDemand;
				
				// styles
				if (!string.IsNullOrEmpty(cssClass)) { treeView.CssClass = cssClass; }
				if (cssLevelStyles != null && cssLevelStyles.Length > 0)
				{
					foreach (string levelStyle in cssLevelStyles) { treeView.LevelStyles.Add(new TreeNodeStyle() { CssClass = levelStyle }); }
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("drtygcjcxdryfxfdxdffxd", ex);
				treeView = null;
			}
			return treeView;
		}








		public enum NavigationStyle
		{
			Windows7, WindowsXp, SimpleSiteMap, None,
		}

//        /// <summary>
//        /// add classes to head to be use by sitemap tree view
//        /// </summary>
//        private static void addCssToHead()
//        {
//            if (HttpContext.Current != null && HttpContext.Current.Handler != null)
//            {
//                var page = HttpContext.Current.Handler as System.Web.UI.Page;
//                if (page != null && page.Header != null && page.Controls != null)
//                {
//                    page.Header.Controls.Add(new LiteralControl(@"
//<style type='text/css' >
//.navigationSimpleSiteMap td, siteMap span
//{
//	font-size: 10pt;
//	padding: 3px 3px 3px 1px;
//}
//.navigationSimpleSiteMapLevel1
//{
//	font-weight:bold;
//}
//.navigationSimpleSiteMapLevel2
//{
//	font-weight:bold;
//}
///* fixes gaps in asp.net treeView showLines */
//.navigationWindowsXp td div { height: 100% !important; }
//.navigationWindowsXp td img { vertical-align: bottom; }
//
//.navigationWindows7 td, siteMap span
//{
//	padding: 2px 0px 2px 3px;
//}
//.navigationWindows7 td img { vertical-align: bottom; }
//</style>"));
//                }
//                else
//                {
//                    Log.WriteError("e5yrrtutyty", "Failed to get page object to add CSS to header");
//                }
//            }
//        }

		/// <summary>
		/// Create a TreeView control from passed PortalSiteMapProvider
		/// </summary>
		/// <param name="provider">for SharePoint 2010, this should be 
		/// PortalSiteMapProvider.CombinedNavSiteMapProvider unless you have a custom provider</param>
		/// <param name="rootNodeAbsoluteUrl">If null, should use the root web url</param>
		/// <param name="expandLevelsBelowRootNode">number of levels to expand below root node. 0 = none, 1 = 2 levels, the root level and 1 level below it.</param>
		/// <param name="isPopulateNodesOnDemand">If true, the nodes below defined thresholds will dynamically load when click on expand icon</param>
		/// <param name="isPrefetchNodesToHideExpandIconWhenNoChildren">Will prefetch grandchild nodes to see if the child nodes can be expanded. (If not, will not display expand icon)</param>
		/// <param name="isShowLines"></param>
		/// <param name="nodeIconUrl">Additional icon to appear to the left of the node text (i.e., webpage icon)</param>
		/// <param name="cssClass">class for the container div</param>
		/// <param name="cssLevelStyles">class assigned to each level (first class assigned to root node)</param>
		/// <returns></returns>
		public static TreeView CreateSiteMapTreeView(PortalSiteMapProvider provider, 
			string rootNodeAbsoluteUrl, int expandLevelsBelowRootNode, 
			bool isPopulateNodesOnDemand, bool isPrefetchNodesToHideExpandIconWhenNoChildren,
			bool isShowLines, string nodeIconUrl, 
			string cssClass, string[] cssLevelStyles)
		{
			// suppress access denied errors
			bool originalErrorCatchValue = SPSecurity.CatchAccessDeniedException; 
			SPSecurity.CatchAccessDeniedException = false;
			TreeView treeView = null;
			bool isDebug = false;
			try
			{
				if (isDebug && provider != null) { Log.WriteInformation("Provider: " + provider.Name + ", security trimmed? " + provider.SecurityTrimmingEnabled); }
				// create empty tree view control
				treeView = CreateTreeView(null, expandLevelsBelowRootNode, isPopulateNodesOnDemand, isShowLines,
					cssClass, cssLevelStyles);
				if (treeView == null) { throw new Exception("Failed to create TreeView control"); }

				// add root node
				if (string.IsNullOrEmpty(rootNodeAbsoluteUrl)) { rootNodeAbsoluteUrl = provider.CurrentSite.Url; }
				var rootNode = GetNode(provider, rootNodeAbsoluteUrl);
				if (rootNode == null) { throw new Exception("Failed to find root node: " + rootNodeAbsoluteUrl); }
				if (isDebug) { Log.WriteInformation("Root node: " + rootNode.Url); }
				var rootTreeNode = new TreeNode();
				rootTreeNode.Text = rootNode.Title;
				rootTreeNode.NavigateUrl = rootNode.Url;
				rootTreeNode.PopulateOnDemand = true;
				treeView.Nodes.Add(rootTreeNode);
				

				// does nothing, but needs to be here for expand to work
				treeView.TreeNodeExpanded += new TreeNodeEventHandler(delegate(object sender, TreeNodeEventArgs e) { });

				// event when user expands node
				treeView.TreeNodePopulate += new TreeNodeEventHandler(
					delegate (object sender, TreeNodeEventArgs e)
					{
						string currentNodeUrl = null;
						try
						{
							// only get child nodes if doesn't already have them
							if (e.Node.ChildNodes.Count == 0)
							{
								// init
								currentNodeUrl = e.Node.NavigateUrl;

								// get current node
								PortalSiteMapNode currentNode = GetNode(provider, e.Node.NavigateUrl);
								if (currentNode == null) { throw new Exception("Failed to find PortalSiteMapNode using asp.net node url: " + e.Node.NavigateUrl); }
								if (isDebug) { Log.WriteInformation("Node: " + currentNode.Url + ", logged in user can access? " + currentNode.IsAccessibleToUser(HttpContext.Current)); }

								// get child nodes (forces get from sql if not in-memory) Note that is is different from childNodes = currentNode.ChildNodes
								List<SiteMapNode> childNodes = GetNodeChildren(provider, currentNode, false);

								// create child nodes
								if (childNodes != null && childNodes.Count > 0)
								{
									if (isDebug) { Log.WriteInformation("Found " + childNodes.Count + " children for node: " + e.Node.NavigateUrl); }
									foreach (SiteMapNode childNode in childNodes)
									{
										currentNodeUrl = childNode.Url;

										TreeNode n = new TreeNode(nodeImageHtml(nodeIconUrl) + childNode.Title);
										n.NavigateUrl = childNode.Url;

										// check for child nodes? (If not check, then assume has children)
										bool isHasChildNodes = true;
										if (IsExternalUrl(childNode)) 
										{ 
											isHasChildNodes = false;
											if (isDebug) { Log.WriteInformation("Child node is an external link, will not look for it's child nodes. Link: " + childNode.Url); }
										}
										if (isHasChildNodes && isPrefetchNodesToHideExpandIconWhenNoChildren)
										{
											if (isDebug) { Log.WriteInformation("Searching for grandchild nodes to see if this child node needs an expand link"); }
											if ((e.Node.Depth + 1) >= expandLevelsBelowRootNode)
											{
												isHasChildNodes = false;

												// convert current child node to portal sitemap node
												PortalSiteMapNode _childNode = GetNode(provider, childNode.Url);
												if (_childNode != null)
												{
													List<SiteMapNode> _grandChildNodes = GetNodeChildren(provider, 
														_childNode, false);
													if (_grandChildNodes != null && _grandChildNodes.Count > 0)
													{
														isHasChildNodes = true;
														if (isDebug) { Log.WriteInformation("Node: " + _childNode.Url + " has grandchildren"); }
													}
												}
												else
												{
													// error occured in converting child node to Portal SiteMap node
													isHasChildNodes = true;
												}

											} // node depth is at or below the expand level

										} // check for child nodes

										// show expand icon?
										if (isHasChildNodes)
										{
											n.PopulateOnDemand = true;
											n.SelectAction = TreeNodeSelectAction.Expand;
										}
										else
										{
											n.PopulateOnDemand = false;
										}

										// add child node to collection
										e.Node.ChildNodes.Add(n);

									} // foreach child node

								} // fetched children
							}
						}
						catch (Exception ex)
						{
							Log.WriteError("dfsytfutrseysteee", ex, "currentNode: " + currentNodeUrl);
						}
					}
					);
			}
			catch (Exception ex)
			{
				Log.WriteError("srdsgHGsejggyfudry", ex);
				treeView = null;
			}
			finally
			{
				SPSecurity.CatchAccessDeniedException = originalErrorCatchValue;
			}
			return treeView;
		}

		public static bool IsExternalUrl(SiteMapNode n)
		{
			bool _isExternalUrl = false;
			if (n != null)
			{
				if (n.GetType() == typeof(Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode))
				{
					_isExternalUrl = ((Microsoft.SharePoint.Publishing.Navigation.
						SPNavigationSiteMapNode)(n)).IsExternal;
				}
			}
			return _isExternalUrl;
		}

		private static string nodeImageHtml(string nodeIconUrl)
		{
			if (!string.IsNullOrEmpty(nodeIconUrl))
			{
				return "<img style=\"border-width:0; margin-right:3px;\" alt=\"\" src=\"" + nodeIconUrl + "\">";
			}
			else
			{
				return string.Empty;
			}
		}

		public static PortalSiteMapNode GetNode(PortalSiteMapProvider provider, string url)
		{
			// get current node
			PortalSiteMapNode node = provider.FindSiteMapNode(url) as PortalSiteMapNode;
			if (node == null) { Log.WriteError("rsytuyitysrsddf", "Failed to find PortalSiteMapNode using url: " + url); }

			return node;
		}

		public static List<SiteMapNode> GetNodeChildren(PortalSiteMapProvider provider, PortalSiteMapNode node, 
			bool isSort)
		{
			List<SiteMapNode> nodes = new List<SiteMapNode>();
			SiteMapNodeCollection nodesCollection = null;
				
			// get all child nodes
			nodesCollection = node.ChildNodes;
			
			//// get child nodes found in cache, with at least read access
			//nodesCollection = provider.GetChildNodes(node, NodeTypes.Area | NodeTypes.AuthoredLink | NodeTypes.Page, NodeTypes.None);

			// convert nodes into generic collection
			if (nodesCollection != null && nodesCollection.Count > 0)
			{
				foreach (SiteMapNode n in nodesCollection) { nodes.Add(n); }

				// sort
				if (isSort)
				{
					nodes.Sort(delegate(SiteMapNode n1, SiteMapNode n2) { return n1.Title.CompareTo(n2.Title); });
				}
			}
			return nodes;
		}











		///// <summary>
		///// Create SiteMap Html to output
		///// http://stackoverflow.com/questions/1039448/creating-sharepoint-moss-sitemap
		///// http://blogs.msdn.com/b/ecm/archive/2007/05/23/increased-performance-for-moss-apps-using-the-portalsitemapprovider.aspx
		///// </summary>
		//public static string GetSiteMap(SPWeb webRootNode, bool isIncludeRootNode, int maxLevelsBelowRoot, bool isIncludePages, bool isIncludeAuthoredLinks,
		//    string rootNodeStyle)
		//{
		//    string o = null;
		//    try
		//    {
		//        // init
		//        if (PortalSiteMapProvider.WebSiteMapProvider != null)
		//        {
		//            // use static member to reference in-memory cached provider
		//            PortalSiteMapProvider siteMapProvider = PortalSiteMapProvider.WebSiteMapProvider;

		//            // properties
		//            siteMapProvider.DynamicChildLimit = 0;
		//            siteMapProvider.EncodeOutput = true;
		//            siteMapProvider.IncludeHeadings = false;

		//            // include pages?
		//            if (isIncludePages) { siteMapProvider.IncludePages = PortalSiteMapProvider.IncludeOption.Always; }
		//            else { siteMapProvider.IncludePages = PortalSiteMapProvider.IncludeOption.Never; }

		//            // authored links?
		//            siteMapProvider.IncludeAuthoredLinks = isIncludeAuthoredLinks;

		//            // get tree root
		//            PortalSiteMapNode rootNode = null;
		//            if (webRootNode != null) { rootNode = (PortalSiteMapNode)siteMapProvider.FindSiteMapNode(webRootNode.Url); }
		//            else { rootNode = (PortalSiteMapNode)siteMapProvider.FindSiteMapNode(SPContext.Current.Site.RootWeb.Url); }
		//            if (rootNode.Type != NodeTypes.Area) { throw new Exception("Root node passed must be an SPWeb!"); }

		//            if (isIncludeRootNode)
		//            {
		//                o = "<ul class=\"siteMap\"><li>" + formatNodeLink(rootNode, rootNodeStyle);
		//                rootNodeStyle = null; // prevent from being used again
		//            }

		//            int currentLevel = 1;
		//            o += getChildNodesFormatted(siteMapProvider, rootNode, currentLevel, maxLevelsBelowRoot, rootNodeStyle, null);

		//            if (isIncludeRootNode) { o += "</li></ul>"; }
		//        }
		//    }
		//    catch (Exception ex)
		//    {
		//        Log.WriteError("srdjjdtddszsetfuuy7h", ex);
		//        o = null;
		//    }
		//    return o;
		//}

		//private static string getChildNodesFormatted(PortalSiteMapProvider siteMapProvider, PortalSiteMapNode node,
		//    int currentLevel, int maxLevels, string styleDirectChildren, string styleGrandChildren)
		//{
		//    bool isNodeUsed = false;
		//    string o = null;

		//    // only get child if less than or equal to max levels below root OR max levels defined as 0
		//    if (maxLevels == 0 || (currentLevel <= maxLevels))
		//    {
		//        currentLevel++;

		//        // get child nodes
		//        System.Web.SiteMapNodeCollection childNodes = siteMapProvider.GetChildNodes(node, NodeTypes.Area, NodeTypes.None);

		//        foreach (PortalSiteMapNode childNode in childNodes)
		//        {
		//            if (childNode.Type != NodeTypes.Heading && !string.IsNullOrEmpty(childNode.Title))
		//            {
		//                isNodeUsed = true;
		//                o += "<li>" + formatNodeLink(childNode, styleDirectChildren);

		//                // recursion
		//                if (childNode.HasChildNodes)
		//                {
		//                    o += getChildNodesFormatted(siteMapProvider, childNode, currentLevel, maxLevels, styleGrandChildren, styleGrandChildren);
		//                }
		//                o += "</li>";
		//            }
		//        }

		//        // was any nodes used?
		//        if (isNodeUsed) { o = "<ul class=\"siteMap\">" + o + "</ul>"; } else { o = null; }
		//    }
		//    return o;
		//}

		//private static string formatNodeLink(PortalSiteMapNode node, string style)
		//{
		//    string o = null;
		//    if (node != null)
		//    {
		//        if (!string.IsNullOrEmpty(style)) { style = " style=\"" + style + "\""; }
		//        o = string.Format("<a href=\"{0}\" title=\"{1}\"{2}>{1}</a>", node.Url, node.Title, style);
		//        //if (isIncludeIcon)
		//        //{
		//        //    string iconHtml = null;
		//        //    if (node.Type == NodeTypes.Area)
		//        //    {
		//        //        iconHtml = "<img style=\"border-width:0; margin-right:3px;\" alt=\"\" src=\"/_layouts/images/SharePointFoundation16.png\">";
		//        //    }
		//        //    o = iconHtml + o;
		//        //}
		//    }
		//    return o;
		//}


	}
}
